#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/miscdevice.h>
#include <linux/device.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <asm/uaccess.h>
#include <asm/delay.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/mutex.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/dma-contiguous.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/sched/signal.h>
#include <linux/file.h>
#include <linux/seq_file.h>
#include <linux/kobject.h>
#include <linux/kmod.h>
#include <linux/sched.h>
#include <linux/signal.h>
#include <linux/err.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <linux/cma.h>
#include <linux/time.h>
#include <linux/atomic.h>
#include <linux/delay.h>

#define DEVICE_NAME "dma_driver"
#define ImageReadAddress0 0x40000000

volatile unsigned int *CaptureReadAddr0;

struct timeval tb, te;

#define MM_SIZE (4096)

void dma_callback_func(void *dma_async_param);
void dma_read(u32 dma_dst, u32 dma_src, int size);
void dma_init(struct platform_device *pdev, u32 s, int size);
void dma_del(void);

struct dma_chan *chan;
dma_addr_t dma_src;
dma_addr_t dma_dst;
char *src = NULL;
char *dst = NULL;
struct dma_device *dev;
struct dma_async_tx_descriptor *tx = NULL;
enum dma_ctrl_flags flags;
dma_cookie_t cookie;

// When DMA transfer finished, this function will be called.
void dma_callback_func(void *dma_async_param)
{
    int i = 0;

    printk("[%s][%d] 1441================\r\n", __func__, __LINE__);

    return;
    for (i = 0; i < MM_SIZE; i++) {
        if (*(dst + i) != (char)('a' + i % 26)) {
            printk("Failed\n");
            return;
        }
    }

    printk("[%s][%d] 33333================\r\n", __func__, __LINE__);
    printk("PASS\n");
}

void dma_read(u32 dst_1, u32 src, int size)
{
    int status;
    int i;

    tx = dev->device_prep_dma_memcpy(chan, dst_1, src, size, flags);
    if (!tx) {
        printk(KERN_INFO "Failed to prepare DMA memcpy");
        return;
    }

    tx->callback = dma_callback_func;
    tx->callback_param = NULL;
    cookie = tx->tx_submit(tx);
    if (dma_submit_error(cookie)) {
        printk(KERN_INFO "Failed to do DMA tx_submit");
        return;
    }

    dma_async_issue_pending(chan);
    status = dma_wait_for_async_tx(tx);

    if (status != DMA_COMPLETE) {
        printk("Failed to complete DMA transfer\n");
        return;
    }

    for (i = 0; i < MM_SIZE; i++) {
        if (*(dst + i) != (char)('a' + i % 26)) {
            printk("Failed\n");
            return;
        }
    }

    printk("PASS ==================22222\n");
}

void dma_init(struct platform_device *pdev, u32 s, int size)
{
    dma_cap_mask_t mask;
    struct device *mydev = &pdev->dev;

    dma_src = s;
    dst = dma_alloc_coherent(mydev, size, &dma_dst, GFP_KERNEL);
    if (dst == NULL) {
        printk(KERN_INFO "dst = NULL! ============== \r\n");
        return;
    }

    dma_cap_zero(mask);
    dma_cap_set(DMA_SLAVE, mask);

    chan = dma_request_channel(mask, NULL, NULL);
    if (chan == NULL) {
        printk(KERN_INFO "chan = NULL! ============== \r\n");
        return;
    }

    flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
    dev = chan->device;
}

void dma_del(void)
{
    dma_free_coherent(NULL, MM_SIZE, dst, &dma_dst);
    dma_release_channel(chan);
}

static int device_open(struct inode *inode, struct file *file)
{
    return 0;
}

static int device_close(struct inode *inode, struct file *file)
{
    printk("device close\n");
    return 0;
}

static ssize_t device_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
    int ret = 0;

    dma_read(dma_dst, dma_src, MM_SIZE);

    return ret;
}

static struct file_operations device_fops = {
    .owner = THIS_MODULE,
    .open = device_open,
    .release = device_close,
    .read = device_read,
};

static struct miscdevice MMAP_misc = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = DEVICE_NAME,
    .fops = &device_fops,
};

static int my_probe(struct platform_device *pdev)
{
    int ret = 0;
    int i = 0;

    ret = misc_register(&MMAP_misc);
    if (ret) {
        printk("Error: misc_register failed!\n");
        return ret;
    }

    CaptureReadAddr0 = (volatile unsigned int *)ioremap(ImageReadAddress0, MM_SIZE);
    if (NULL == CaptureReadAddr0) {
        printk("[%s][%d] ================\r\n", __func__, __LINE__);
        return -ENOMEM;
    }

    src = (char *)CaptureReadAddr0;
    dma_init(pdev, ImageReadAddress0, MM_SIZE);

    for (i = 0; i < MM_SIZE; i++) {
        *(src + i) = (char)('a' + i % 26);
    }

    dma_read(dma_dst, dma_src, MM_SIZE);

    return 0;
}

static int my_remove(struct platform_device *pdev)
{
    iounmap(CaptureReadAddr0);
    dma_del();
    misc_deregister(&MMAP_misc);
    return 0;
}

static const struct of_device_id my_match[] = {
    { .compatible = "my-virtual-device" },
    { /* end of list */ }
};
MODULE_DEVICE_TABLE(of, my_match);

static struct platform_driver my_driver = {
    .probe = my_probe,
    .remove = my_remove,
    .driver = {
        .name = "my-virtual-device",
        .of_match_table = my_match,
    },
};

module_platform_driver(my_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("DMA_test");
